/****************************************************************************
**
****************************************************************************/

#include <QProcess>
#include <QSettings>
#include <QFile>
#include <QDir>
#include <QNetworkReply>
#include <QNetworkAccessManager>

#include "util.h"
#include "baseUpdate.h"

#include <QDebug>
#define UPDATE_DEBUG

double BaseUpdate::progress_;
BaseUpdate::State BaseUpdate::state_ = BaseUpdate::kInvalid;

qint32 BaseUpdate::version_;
qint32 BaseUpdate::subversion_;
qint32 BaseUpdate::revision_;

BaseUpdate::BaseUpdate(QObject *parent) : QObject(parent),
    m_quit(false)
{
#ifdef __arm__
    QString path = "./ota";
    QDir dir(path);
    if (!dir.exists()) {
        dir.mkpath(path);
        qDebug() << "[UPDATE]-> mkpath ota files";
    }
#endif

    m_otaManager = new QNetworkAccessManager(this);
    m_otaUrl = new QUrl;
    m_otaFile = new QFile;

    InitializationVersion(TCU_SOFTWARE_VERSION, TCU_SOFTWARE_SUBVERSION, TCU_SOFTWARE_REVISION);

    connect(m_otaManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(DownloadReplyFinished(QNetworkReply *)));
}

BaseUpdate::~BaseUpdate()
{
    m_quit = true;
    wait();

    if (nullptr != m_otaFile) {
        delete m_otaFile;
        m_otaFile = nullptr;
    }

    if (nullptr != m_otaUrl) {
        delete m_otaUrl;
        m_otaUrl = nullptr;
    }
}

void BaseUpdate::InitializationVersion(qint32 version, qint32 subversion, qint32 revision)
{
    QSettings settings("./ota/info.ini", QSettings::IniFormat);

    version_ = settings.value("info/version", version).toInt();
    subversion_ = settings.value("info/subversion", subversion).toInt();
    revision_ = settings.value("info/revision", revision).toInt();
}

void BaseUpdate::ConfigureVersion(qint32 version, qint32 subversion, qint32 revision)
{
    QSettings settings("./ota/info.ini", QSettings::IniFormat);

    settings.setValue("info/version", version);
    settings.setValue("info/subversion", subversion);
    settings.setValue("info/revision", revision);

    version_ = version;
    subversion_ = subversion;
    revision_ = revision;
}

bool BaseUpdate::CheckUrl()
{
    if (m_otaUrl == nullptr) {
        return false;
    }

    bool result = true;

    if (!m_otaUrl->isValid()) {
        result = false;
    }

    return result;
}

bool BaseUpdate::CheckSavePath()
{
    if (m_otaFile == nullptr) {
        return false;
    }

    bool result = true;

    if (m_otaFile->fileName().isEmpty()) {
        result = false;
    }

    return result;
}

void BaseUpdate::SetNamePassword(const QString name, const QString password)
{
    m_otaUrl->setUserName(name);
    m_otaUrl->setPassword(password);
}

void BaseUpdate::SetUrl(const QString url)
{
    m_otaUrl->setUrl(url);
}

void BaseUpdate::CreateDownloadFile(const QString name)
{
    if (name.isEmpty()) {
        return;
    }

    if (m_otaFile == nullptr) {
        return;
    }

    m_otaFile->setFileName("./ota/" + name);
}

quint16 BaseUpdate::CheckCRC()
{
    return 0x00;
}

void BaseUpdate::StartDownloadFile(qint32 version, qint32 subversion, qint32 revision, qint32 packLength, qint32 checkSum, QString updateAddress)
{
#if defined(UPDATE_DEBUG)
    qDebug() << "[UPDATE]-> ready download update file:"
             << "ver" << version
             << "subver" << subversion
             << "revision" << revision
             << "packLength" << packLength
             << "checkSum" << checkSum
             << "address" << updateAddress;
#endif

    if ((version <= 0) && (subversion <= 0) && (revision <= 0)) {
        return;
    }
    m_downloadInfo.version = version;
    m_downloadInfo.subversion = subversion;
    m_downloadInfo.revision = revision;

    if (packLength <= 0) {
        return;
    }
    m_downloadInfo.packLength = packLength;

    if (checkSum <= 0) {
        return;
    }
    m_downloadInfo.checkSum = checkSum;

    if (updateAddress.isEmpty()) {
#if defined(UPDATE_DEBUG)
        qDebug() << "[UPDATE]-> update address is empty";
#endif
        return;
    }
    SetUrl(updateAddress);
    if (!CheckUrl()) {
#if defined(UPDATE_DEBUG)
        qDebug() << "[UPDATE]-> update url check error";
#endif
        return;
    }

    if ((0 < m_downloadInfo.version) && (5 >= m_downloadInfo.version)) {
        m_downloadInfo.updateType = "tcu";
    } else if (6 == m_downloadInfo.version) {
        m_downloadInfo.updateType = "ccu";
    } else if (7 == m_downloadInfo.version) {
        m_downloadInfo.updateType = "awcu";
    } else if (8 == m_downloadInfo.version) {
        m_downloadInfo.updateType = "bwcu";
    } else {
        m_downloadInfo.updateType = "invalid";
        return;
    }
    CreateDownloadFile(m_downloadInfo.updateType);
    if (!CheckSavePath()) {
#if defined(UPDATE_DEBUG)
        qDebug() << "[UPDATE]-> ota download path error";
#endif
        return;
    }

    if (!m_otaFile->open(QIODevice::WriteOnly)) {
#if defined(UPDATE_DEBUG)
        qDebug() << "[UPDATE]-> ota file open error";
#endif
        return;
    }

#if defined(UPDATE_DEBUG)
    qDebug() << "[UPDATE]-> start download update file, url:" << m_otaUrl->url();
#endif

    m_downloadReply = m_otaManager->get(QNetworkRequest(*m_otaUrl));
    if (m_downloadReply == nullptr) {
        return;
    }

    //big-file download if
    connect(m_downloadReply, &QNetworkReply::readyRead, this, &BaseUpdate::DownloadReadyRead);
    //result error
    connect(m_downloadReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(DownloadError(QNetworkReply::NetworkError)));
}

void BaseUpdate::DownloadReadyRead()
{
    if (m_downloadReply == nullptr) {
        return;
    }

    QByteArray byteArray = m_downloadReply->readAll();
    if (byteArray.size() <= 0) {
        return;
    }
    m_otaFile->write(byteArray);
    qint64 size = m_otaFile->size();

    set_state(kUpdateing);
    set_progress(size * 100.0 / m_downloadInfo.packLength);

#if defined(UPDATE_DEBUG)
    qDebug() << "[UPDATE]-> download update file... fsize:" << size;
#endif
}

void BaseUpdate::DownloadError(QNetworkReply::NetworkError code)
{
#if defined(UPDATE_DEBUG)
    qDebug() << "[UPDATE]-> download update file failed, ecode:" << static_cast<int>(code);
#endif
}

void BaseUpdate::DownloadReplyFinished(QNetworkReply *reply)
{
    if (nullptr == reply) {
        m_otaFile->close();
        return;
    }

    reply->deleteLater();
    reply = nullptr;

    qint64 size = m_otaFile->size();
    if (m_downloadInfo.packLength != size) {
        m_otaFile->close();
        m_otaFile->remove();
#if defined(UPDATE_DEBUG)
        qDebug() << "[UPDATE]-> update file size error";
#endif
        return;
    }

    /*
    quint16 crc = checkCRC();
    if (m_checkSum != crc) {
        m_otaFile->close();
    #if defined(OTA_DEBUG)
        qDebug() << "[UPDATE]-> update file crc error";
    #endif
        return;
    } */

#if defined(UPDATE_DEBUG)
    qDebug() << "[UPDATE]-> download update file finished";
#endif

    if (m_otaFile->isOpen()) {
        m_otaFile->close();
    }

    if ("tcu" == m_downloadInfo.updateType) {
        InitializationVersion(m_downloadInfo.version, m_downloadInfo.subversion, m_downloadInfo.revision);

        set_state(kSuccess);
        set_progress(100.0);

        QProcess::execute("reboot");

    } else if (("ccu" == m_downloadInfo.updateType) || ("awcu" == m_downloadInfo.updateType) || ("bwcu" == m_downloadInfo.updateType)) {
        emit DownloadFinish(m_downloadInfo.subversion, m_downloadInfo.revision, m_downloadInfo.packLength, m_downloadInfo.checkSum, m_downloadInfo.updateType);
    } else {
        //...
    }
}
